// DiplomacyScreen.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

using BindingOriented.SyncLinq;

using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;

using Supremacy.Annotations;
using Supremacy.Client.Dialogs;
using Supremacy.Client.Views;
using Supremacy.Collections;
using Supremacy.Diplomacy;
using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.Messages;
using Supremacy.Messaging;
using Supremacy.Resources;
using Supremacy.Types;
using Supremacy.UI;
using Supremacy.Universe;
using System.Text;

using Supremacy.Utility;

namespace Supremacy.Client
{
    public enum DiplomacyScreenViewType
    {
        Overview,
        Agreements,
        Inbox,
        Outbox
    }

    [TemplatePart(Name = "PART_CivRelationshipGraph", Type = typeof(NodeGraph))]
    [TemplatePart(Name = "PART_MessageTextPresenter", Type = typeof(ContentPresenter))]
    [TemplatePart(Name = "PART_ProposalOffersLeadInPresenter", Type = typeof(ContentPresenter))]
    [TemplatePart(Name = "PART_ProposalDemandsLeadInPresenter", Type = typeof(ContentPresenter))]
    [TemplatePart(Name = "PART_StipulationList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_MutualStipulationList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_OfferStipulationList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_DemandStipulationList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_IncomingMessageList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_CivilizationList", Type = typeof(ItemsControl))]
    [TemplatePart(Name = "PART_CommDetailsBorder", Type = typeof(Decorator))]
    public class DiplomacyScreen : GameScreen<DiplomacyScreenPresentationModel>, IDiplomacyScreenView
    {
        public static readonly DependencyPropertyKey IsCurrentMessageNullPropertyKey;
        public static readonly DependencyPropertyKey IsCurrentMessageIncomingPropertyKey;
        public static readonly DependencyPropertyKey IsCurrentMessageOutgoingPropertyKey;
        public static readonly DependencyPropertyKey IsCurrentMessageDraftPropertyKey;
        public static readonly DependencyPropertyKey IsCurrentRelationshipNullPropertyKey;
        public static readonly DependencyPropertyKey CurrentRelationshipPropertyKey;
        public static readonly DependencyPropertyKey CurrentMessagePropertyKey;
        public static readonly DependencyProperty CurrentViewProperty;
        public static readonly DependencyProperty OutboxDraftProperty;
        public static readonly DependencyPropertyKey HasOutboxDraftPropertyKey;
        public static readonly DependencyProperty ReplyDraftProperty;
        public static readonly DependencyPropertyKey HasReplyDraftPropertyKey;
        public static readonly RoutedCommand SetCurrentViewCommand;
        public static readonly RoutedCommand SetMessageParameterCommand;
        public static readonly RoutedCommand SetStipulationParameterCommand;
        public static readonly RoutedCommand ResetDiplomacyGraphCommand;

        public static readonly RoutedCommand SendMessageCommand;
        public static readonly RoutedCommand EditMessageCommand;
        public static readonly RoutedCommand CancelMessageCommand;

        public static readonly RoutedCommand AddStipulationCommand;
        public static readonly RoutedCommand RemoveStipulationCommand;

        public static readonly RoutedCommand AcceptProposalCommand;
        public static readonly RoutedCommand RejectProposalCommand;
        public static readonly RoutedCommand CounterProposalCommand;

        public static readonly RoutedCommand CommendCommand;
        public static readonly RoutedCommand DenounceCommand;
        public static readonly RoutedCommand IssueThreatCommand;
        public static readonly RoutedCommand DeclareWarCommand;
        public static readonly RoutedCommand MakeProposalCommand;

        public static readonly RoutedCommand TerminateAgreementCommand;

        static DiplomacyScreen()
        {
            IsCurrentMessageNullPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsCurrentMessageNull",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(true));
            IsCurrentMessageIncomingPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsCurrentMessageIncoming",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(false));
            IsCurrentMessageOutgoingPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsCurrentMessageOutgoing",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(false));
            IsCurrentMessageDraftPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsCurrentMessageDraft",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(false));
            IsCurrentRelationshipNullPropertyKey = DependencyProperty.RegisterReadOnly(
                "IsCurrentRelationshipNull",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(true));
            CurrentRelationshipPropertyKey = DependencyProperty.RegisterReadOnly(
                "CurrentRelationship",
                typeof(Relationship),
                typeof(DiplomacyScreen),
                new PropertyMetadata(null, CurrentRelationshipChangedCallback));
            CurrentMessagePropertyKey = DependencyProperty.RegisterReadOnly(
                "CurrentMessage",
                typeof(DiplomaticMessage),
                typeof(DiplomacyScreen),
                new PropertyMetadata(null, CurrentMessageChangedCallback));
            CurrentViewProperty = DependencyProperty.Register(
                "CurrentView",
                typeof(DiplomacyScreenViewType),
                typeof(DiplomacyScreen),
                new PropertyMetadata(
                    DiplomacyScreenViewType.Overview,
                    CurrentViewChangedCallback));
            OutboxDraftProperty = DependencyProperty.Register(
                "OutboxDraft",
                typeof(DiplomaticMessage),
                typeof(DiplomacyScreen),
                new PropertyMetadata(
                    null,
                    OutboxDraftChangedCallback));
            HasOutboxDraftPropertyKey = DependencyProperty.RegisterReadOnly(
                "HasOutboxDraft",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(false));
            ReplyDraftProperty = DependencyProperty.Register(
                "ReplyDraft",
                typeof(DiplomaticMessage),
                typeof(DiplomacyScreen),
                new PropertyMetadata(
                    null,
                    ReplyDraftChangedCallback));
            HasReplyDraftPropertyKey = DependencyProperty.RegisterReadOnly(
                "HasReplyDraft",
                typeof(bool),
                typeof(DiplomacyScreen),
                new PropertyMetadata(false));
            ResetDiplomacyGraphCommand = new RoutedCommand(
                "ResetDiplomacyGraph",
                typeof(DiplomacyScreen));
            SetCurrentViewCommand = new RoutedCommand(
                "SetCurrentView",
                typeof(DiplomacyScreen));
            SetMessageParameterCommand = new RoutedCommand(
                "SetMessageParameter",
                typeof(DiplomacyScreen));
            SendMessageCommand = new RoutedCommand(
                "SendMessage",
                typeof(DiplomacyScreen));
            EditMessageCommand = new RoutedCommand(
                "EditMessage",
                typeof(DiplomacyScreen));
            CancelMessageCommand = new RoutedCommand(
                "CancelMessage",
                typeof(DiplomacyScreen));
            AddStipulationCommand = new RoutedCommand(
                "AddStipulation",
                typeof(DiplomacyScreen));
            RemoveStipulationCommand = new RoutedCommand(
                "RemoveStipulation",
                typeof(DiplomacyScreen));
            SetStipulationParameterCommand = new RoutedCommand(
                "SetStipulationParameter",
                typeof(DiplomacyScreen));
            CommendCommand = new RoutedCommand(
                "Commend",
                typeof(DiplomacyScreen));
            DenounceCommand = new RoutedCommand(
                "Denounce",
                typeof(DiplomacyScreen));
            IssueThreatCommand = new RoutedCommand(
                "IssueThreat",
                typeof(DiplomacyScreen));
            DeclareWarCommand = new RoutedCommand(
                "DeclareWar",
                typeof(DiplomacyScreen));
            MakeProposalCommand = new RoutedCommand(
                "MakeProposal",
                typeof(DiplomacyScreen));
            AcceptProposalCommand = new RoutedCommand(
                "AcceptProposal",
                typeof(DiplomacyScreen));
            RejectProposalCommand = new RoutedCommand(
                "RejectProposal",
                typeof(DiplomacyScreen));
            CounterProposalCommand = new RoutedCommand(
                "CounterProposal",
                typeof(DiplomacyScreen));
            TerminateAgreementCommand = new RoutedCommand(
                "TerminateAgreement",
                typeof(DiplomacyScreen));
        }

        private static void CurrentMessageChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var source = d as DiplomacyScreen;
            if (source == null)
                return;

            var message = e.NewValue as DiplomaticMessage;
            if (message != null)
            {
                var otherCiv = (message.Sender == source.PlayerCiv) ? message.Recipient : message.Sender;
                if (otherCiv != null)
                    source.CurrentRelationship = GameContext.Current.Relationships[source.PlayerCiv, otherCiv];

                if (message.IsProposal && (message.Recipient == source.PlayerCiv))
                {
                    DiplomaticMessage tempMessage = null;

                    if (source.HasReplyDraft && (source.ReplyDraft.Proposal == message.Proposal))
                        tempMessage = source.ReplyDraft;

                    if (source.CurrentRelationship.HasOutgoingReply
                        && (source.CurrentRelationship.ReplyOutbox.Proposal == message.Proposal))
                    {
                        tempMessage = source.CurrentRelationship.ReplyOutbox;
                    }

                    if (tempMessage != null)
                    {
                        if (tempMessage != CollectionViewSource.GetDefaultView(source._messages).CurrentItem)
                            CollectionViewSource.GetDefaultView(source._messages).MoveCurrentToPosition(-1);

                        source.CurrentMessage = tempMessage;
                        return;
                    }
                }
            }

            source.UpdateCurrentMessageStats();
            source.UpdateMessage();
        }

        private static void CurrentRelationshipChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiplomacyScreen source = d as DiplomacyScreen;
            if (source != null)
            {
                bool foundMessage = false;
                Relationship relationship = e.NewValue as Relationship;
                if (relationship != null)
                {
                    if (source.CurrentView == DiplomacyScreenViewType.Inbox)
                    {
                        if (source.IsCurrentMessageNull)
                        {
                            if (relationship.HasIncomingMessage)
                            {
                                source.CurrentMessage = relationship.Inbox;
                                foundMessage = true;
                            }
                            else if (relationship.HasIncomingReply)
                            {
                                source.CurrentMessage = relationship.ReplyInbox;
                                foundMessage = true;
                            }
                        }
                    }
                    else if (source.CurrentView == DiplomacyScreenViewType.Outbox)
                    {
                        if ((source.IsCurrentMessageNull) && relationship.HasOutgoingMessage)
                        {
                            source.CurrentMessage = relationship.Outbox;
                            foundMessage = true;
                        }
                    }
                }
                if (!foundMessage)
                {
                    source.CurrentMessage = null;
                }
                source.IsCurrentRelationshipNull = (relationship == null);
            }
        }

        private static void ReplyDraftChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiplomacyScreen sender = d as DiplomacyScreen;
            if (sender != null)
            {
                sender.HasReplyDraft = (e.NewValue != null);
                if ((sender.HasReplyDraft) && (sender.CurrentMessage != e.NewValue))
                {
                    sender.CurrentMessage = sender.ReplyDraft;
                }
                else if ((e.OldValue != null) && (sender.CurrentMessage == e.OldValue))
                {
                    sender.CurrentMessage = null;
                }
                else
                {
                    sender.UpdateCurrentMessageStats();
                }
            }
        }

        private static void OutboxDraftChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiplomacyScreen sender = d as DiplomacyScreen;
            if (sender != null)
            {
                sender.HasOutboxDraft = (e.NewValue != null);
                if (sender.HasOutboxDraft && (sender.CurrentMessage != e.NewValue))
                {
                    sender.CurrentMessage = sender.OutboxDraft;
                }
                else if ((e.OldValue != null) && (sender.CurrentMessage == e.OldValue))
                {
                    sender.CurrentMessage = null;
                }
                sender.UpdateCurrentMessageStats();
            }
        }

        private static void CurrentViewChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiplomacyScreen source = d as DiplomacyScreen;
            if (source != null)
                source.OnCurrentViewChanged();
        }

        public event EventHandler CurrentViewChanged;

        private ICollectionView _relationshipsView;
        private readonly ObservableCollection<DiplomaticMessage> _messages;
        private readonly ObservableCollection<StipulationListItem> _stipulations;
        private NodeGraph _civRelationshipGraph;
        private CivRelationshipGraphNode _playerCivNode;
        private ContentPresenter _messageTextPresenter;
        private ContentPresenter _proposalOffersLeadInPresenter;
        private ContentPresenter _proposalDemandsLeadInPresenter;
        private ItemsControl _stipulationList;
        private ItemsControl _mutualStipulationList;
        private ItemsControl _offerStipulationList;
        private ItemsControl _demandStipulationList;
        private ItemsControl _incomingMessageList;
        private ItemsControl _civilizationList;
        private Decorator _commDetailsBorder;
        private IDisposable _turnStartedSubscription;

        public DiplomacyScreenViewType CurrentView
        {
            get { return (DiplomacyScreenViewType)GetValue(CurrentViewProperty); }
            set { SetValue(CurrentViewProperty, value); }
        }

        public DiplomaticMessage OutboxDraft
        {
            get { return GetValue(OutboxDraftProperty) as DiplomaticMessage; }
            set { SetValue(OutboxDraftProperty, value); }
        }

        public bool HasOutboxDraft
        {
            get { return (bool)GetValue(HasOutboxDraftPropertyKey.DependencyProperty); }
            protected set { SetValue(HasOutboxDraftPropertyKey, value); }
        }

        public DiplomaticMessage ReplyDraft
        {
            get { return GetValue(ReplyDraftProperty) as DiplomaticMessage; }
            set { SetValue(ReplyDraftProperty, value); }
        }

        public bool HasReplyDraft
        {
            get { return (bool)GetValue(HasReplyDraftPropertyKey.DependencyProperty); }
            protected set { SetValue(HasReplyDraftPropertyKey, value); }
        }

        public Civilization PlayerCiv
        {
            get { return this.ClientContext.LocalPlayer.Empire; }
        }

        public DiplomacyScreen([NotNull] IUnityContainer container) : base(container)
        {
            AddHandler(
                ButtonBase.ClickEvent,
                new RoutedEventHandler(CivRelationshipGraph_NodeClick));
            CommandBindings.Add(
                new CommandBinding(ResetDiplomacyGraphCommand,
                                   ResetDiplomacyGraphCommand_Executed));
            CommandBindings.Add(
                new CommandBinding(SetCurrentViewCommand,
                                   SetCurrentViewCommand_Executed));
            CommandBindings.Add(
                new CommandBinding(SetMessageParameterCommand,
                                   SetMessageParameterCommand_Executed,
                                   SetMessageParameterCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(SendMessageCommand,
                                   SendMessageCommand_Executed,
                                   SendMessageCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(EditMessageCommand,
                                   EditMessageCommand_Executed,
                                   EditMessageCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(CancelMessageCommand,
                                   CancelMessageCommand_Executed,
                                   CancelMessageCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(AddStipulationCommand,
                                   AddStipulationCommand_Executed,
                                   AddStipulationCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(RemoveStipulationCommand,
                                   RemoveStipulationCommand_Executed,
                                   RemoveStipulationCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(SetStipulationParameterCommand,
                                   SetStipulationParameterCommand_Executed,
                                   SetStipulationParameterCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(CommendCommand,
                                   CommendCommand_Executed,
                                   CommendCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(DenounceCommand,
                                   DenounceCommand_Executed,
                                   DenounceCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(IssueThreatCommand,
                                   IssueThreatCommand_Executed,
                                   IssueThreatCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(DeclareWarCommand,
                                   DeclareWarCommand_Executed,
                                   DeclareWarCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(MakeProposalCommand,
                                   MakeProposalCommand_Executed,
                                   MakeProposalCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(AcceptProposalCommand,
                                   AcceptProposalCommand_Executed,
                                   AcceptProposalCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(RejectProposalCommand,
                                   RejectProposalCommand_Executed,
                                   RejectProposalCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(CounterProposalCommand,
                                   CounterProposalCommand_Executed,
                                   CounterProposalCommand_CanExecute));
            CommandBindings.Add(
                new CommandBinding(TerminateAgreementCommand,
                                   TerminateAgreementCommand_Executed));

            _relationshipsView = new ListCollectionView(new List<Relationship>(0));
            _messages = new ObservableCollection<DiplomaticMessage>();
            _stipulations = new ObservableCollection<StipulationListItem>();

            CollectionViewSource.GetDefaultView(_messages).CurrentChanged += OnIncomingMessageChanged;
            _relationshipsView.CurrentChanged += OnRelationshipChanged;
            _relationshipsView.CurrentChanging += OnRelationshipChanging;

            CollectionViewSource.GetDefaultView(_stipulations).GroupDescriptions.Add(
                new PropertyGroupDescription("Category"));

            this.DataContext = _relationshipsView;

            this.IsVisibleChanged += DiplomacyScreen_IsVisibleChanged;

            this.Loaded += OnLoaded;
            this.Unloaded += OnUnloaded;

            Reset();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (_turnStartedSubscription != null)
                _turnStartedSubscription.Dispose();

            _turnStartedSubscription = Channel<TurnStartedMessage>.Public
                .ObserveOnDispatcher()
                .Subscribe(_ => this.RefreshScreen());
        }
        
        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            if (_turnStartedSubscription != null)
                _turnStartedSubscription.Dispose();

            _turnStartedSubscription = null;
        }

        private void DiplomacyScreen_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (!IsVisible)
                DiscardDrafts(false);
        }

        private DiplomacyContext GetDiplomacyContext(bool isDemand)
        {
            DiplomacyContext context = null;
            if (IsCurrentMessageDraft)
            {
                context = new DiplomacyContext(
                    CurrentMessage.Sender,
                    CurrentMessage.Recipient,
                    isDemand);
            }
            return context;
        }

        private object GetStipulationEditor(Stipulation stipulation, bool isDemand)
        {
            if (stipulation is GiftStipulation)
            {
                return new GiftEditor(
                    (GiftStipulation)stipulation,
                    GameContext.Current.CivilizationManagers[CurrentRelationship.OwnerCiv]);
            }
            if (stipulation.HasParameter || !stipulation.IsDurationFixed)
            {
                return new StipulationEditor(stipulation, GetDiplomacyContext(isDemand));
            }
            return null;
        }

        private void SetStipulationParameterCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            Stipulation stipulation = e.Parameter as Stipulation;
            if ((stipulation != null) && IsCurrentMessageDraft && CurrentMessage.IsProposal)
            {
                bool isDemand = CurrentMessage.Proposal.DemandStipulations.Contains(stipulation);
                e.CanExecute = (GetStipulationEditor(stipulation, isDemand) != null);
            }
        }

        private void SetStipulationParameterCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Stipulation stipulation = e.Parameter as Stipulation;
            if ((stipulation != null) && IsCurrentMessageDraft && CurrentMessage.IsProposal)
            {
                bool isDemand = CurrentMessage.Proposal.DemandStipulations.Contains(stipulation);
                while (!EditStipulation(stipulation, isDemand) && !stipulation.IsParameterValid)
                {
                    MessageDialogResult result = MessageDialog.Show(
                        "Invalid Parameter",
                        "Your selection is invalid.  Disregard this stipulation?",
                        MessageDialogButtons.YesNo);
                    if (result == MessageDialogResult.No)
                    {
                        break;
                    }
                    if (isDemand)
                    {
                        CurrentMessage.Proposal.RemoveDemand(stipulation);
                    }
                    else
                    {
                        CurrentMessage.Proposal.RemoveOffer(stipulation);
                    }
                }
                UpdateMessage();
            }
        }

        private bool EditStipulation(Stipulation stipulation, bool isDemand)
        {
            if (stipulation != null)
            {
                if (!stipulation.HasParameter)
                    return true;
                
                var editor = GetStipulationEditor(stipulation, isDemand);
                if (editor != null)
                {
                    var dialog = new Dialog { Header = stipulation.ShortDescription, Content = editor };
                    var dialogResult = dialog.ShowDialog();
                    return (dialogResult.HasValue && dialogResult.Value);
                }
            }
            return false;
        }

        private void RemoveStipulationCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (IsCurrentMessageDraft && CurrentMessage.IsProposal);
        }

        private void RemoveStipulationCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (IsCurrentMessageDraft && CurrentMessage.IsProposal)
            {
                Stipulation stipulation = e.Parameter as Stipulation;
                if (stipulation != null)
                {
                    if (CurrentMessage.Proposal.OfferStipulations.Contains(stipulation))
                        CurrentMessage.Proposal.RemoveOffer(stipulation);
                    else if (CurrentMessage.Proposal.DemandStipulations.Contains(stipulation))
                        CurrentMessage.Proposal.RemoveDemand(stipulation);
                    UpdateMessage();
                }
            }
        }

        //private bool RemoveStipulationContainerFromDraft(Stipulation stipulation)
        //{
        //    if (stipulation == null)
        //        return false;

        //    var container = FindDescendant(
        //        _commDetailsBorder,
        //        o => (o is TextBlock) && (((TextBlock)o).Tag == stipulation));

        //    if (_demandStipulationList.IsAncestorOf(container))
        //    {
        //        _demandStipulationList.Items.Remove(container);
        //        return true;
        //    }
        //    else if (_mutualStipulationList.IsAncestorOf(container))
        //    {
        //        _mutualStipulationList.Items.Remove(container);
        //        return true;
        //    }
        //    else if (_offerStipulationList.IsAncestorOf(container))
        //    {
        //        _offerStipulationList.Items.Remove(container);
        //        return true;
        //    }

        //    return false;
        //}

        //private DependencyObject FindDescendant(DependencyObject source, Func<DependencyObject, bool> predicate)
        //{
        //    if ((source == null) || predicate(source))
        //        return source;
        //    int childCount = VisualTreeHelper.GetChildrenCount(source);
        //    if (childCount == 0)
        //        return null;
        //    for (int i = 0; i < childCount; i++)
        //    {
        //        DependencyObject result = FindDescendant(VisualTreeHelper.GetChild(source, i), predicate);
        //        if (result != null)
        //            return result;
        //    }
        //    return null;
        //}

        private void AddStipulationCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (!IsCurrentRelationshipNull && IsCurrentMessageOutgoing && IsCurrentMessageDraft && CurrentMessage.IsProposal);
        }

        private void AddStipulationCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (IsCurrentMessageDraft && CurrentMessage.IsProposal)
            {
                StipulationListItem listItem = e.Parameter as StipulationListItem;
                if (listItem != null)
                {
                    bool isDemand = (listItem.Category == StipulationListItem.DemandsCategory);
                    if (EditStipulation(listItem.Value, isDemand))
                    {
                        if (isDemand)
                        {
                            CurrentMessage.Proposal.AddDemand(listItem.Value);
                        }
                        else
                        {
                            CurrentMessage.Proposal.AddOffer(listItem.Value);
                        }
                        UpdateMessage();
                    }
                }
            }
        }

        private object CreateStipulationContainer(Stipulation stipulation)
        {
            if (!IsCurrentMessageNull && (stipulation != null))
            {
                Regex regex = new Regex(@"(\$[_a-zA-Z][_a-zA-Z0-9)]*)|(\$\([^\)]*\))");
                CivString description = new CivString(CurrentMessage.Sender, CivString.DiplomacyCategory, stipulation.LongDescription.Key);
                string[] stipulationParts = regex.Split(description.Value);
                TextBlock stipulationText = new TextBlock();
                stipulationText.TextWrapping = TextWrapping.Wrap;
                stipulationText.Tag = stipulation;
                foreach (string stipulationPart in stipulationParts)
                {
                    Inline inline = null;
                    if (stipulationPart.StartsWith("$(") && stipulationPart.EndsWith(")"))
                    {
                        inline = new Run(stipulationPart.Substring(2, stipulationPart.Length - 3));
                        if (IsCurrentMessageOutgoing && IsCurrentMessageDraft)
                        {
                            Hyperlink link = new Hyperlink(inline);
                            link.Command = SetStipulationParameterCommand;
                            link.CommandParameter = stipulation;
                            inline = link;
                        }
                    }
                    else if (stipulationPart.StartsWith("$"))
                    {
                        if (stipulationPart.Equals("$Parameter", StringComparison.OrdinalIgnoreCase))
                        {
                            if (stipulation.HasParameter && stipulation.IsParameterValid)
                            {
                                string value;
                                if (stipulation.Parameter is Civilization)
                                {
                                    value = ((Civilization)stipulation.Parameter).LongName;
                                }
                                else if (stipulation.Parameter is UniverseObject)
                                {
                                    value = ((UniverseObject)stipulation.Parameter).Name;
                                }
                                else
                                {
                                    value = stipulation.Parameter.ToString();
                                }
                                inline = new Run(value);
                            }
                            else
                            {
                                inline = new Run("...");
                            }
                            if (IsCurrentMessageOutgoing && IsCurrentMessageDraft)
                            {
                                Hyperlink link = new Hyperlink(inline);
                                link.Command = SetStipulationParameterCommand;
                                link.CommandParameter = stipulation;
                                inline = link;
                            }
                        }
                        else if (stipulationPart.Equals("$Sender", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = new Run(CurrentMessage.Sender.LongName);
                        }
                        else if (stipulationPart.Equals("$Recipient", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = new Run(CurrentMessage.Recipient.LongName);
                        }
                        else if (stipulationPart.Equals("$Offerer", StringComparison.OrdinalIgnoreCase))
                        {
                            Civilization offerer;
                            if (stipulation is MembershipStipulation)
                            {
                                offerer = CurrentMessage.Sender.IsEmpire
                                    ? CurrentMessage.Sender
                                    : CurrentMessage.Recipient;
                            }
                            else
                            {
                                offerer = CurrentMessage.Proposal.DemandStipulations.Contains(stipulation)
                                    ? CurrentMessage.Recipient
                                    : CurrentMessage.Sender;
                            }
                            inline = new Run(offerer.LongName);
                        }
                        else if (stipulationPart.Equals("$Receiver", StringComparison.OrdinalIgnoreCase))
                        {
                            Civilization receiver = CurrentMessage.Proposal.DemandStipulations.Contains(stipulation)
                                ? CurrentMessage.Sender
                                : CurrentMessage.Recipient;
                            inline = new Run(receiver.LongName);
                        }
                        else if (stipulationPart.Equals("$Duration", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = new Run(String.Format(stipulation.GetDurationString(), stipulation.Duration));
                        }
                    }
                    else
                    {
                        inline = new Run(stipulationPart);
                    }
                    if (inline != null)
                    {
                        stipulationText.Inlines.Add(inline);
                    }
                }
                if (IsCurrentMessageDraft)
                {
                    Button removeButton = new Button();
                    removeButton.Command = RemoveStipulationCommand;
                    removeButton.CommandParameter = stipulation;
                    removeButton.CommandTarget = removeButton;
                    removeButton.Margin = new Thickness(8, 0, 0, 0);
                    stipulationText.Inlines.Add(removeButton);
                }
                return stipulationText;
            }
            return null;
        }

        protected void SendMessage(DiplomaticMessage message)
        {
            if ((message != null) && !IsCurrentRelationshipNull)
            {
                RetractMessage(message);
                this.PlayerOrderService.AddOrder(new SendDiplomaticMessageOrder(message));
                if (message.IsReply)
                {
                    CurrentRelationship.ReplyOutbox = message;
                }
                else
                {
                    CurrentRelationship.Outbox = message;
                }
            }
        }

        protected void RetractMessage(DiplomaticMessage message)
        {
            if ((message != null) && !IsCurrentRelationshipNull)
            {
                var orders = this.PlayerOrderService.Orders.ToList();
                for (int i = 0; i < orders.Count; i++)
                {
                    var sendOrder = orders[i] as SendDiplomaticMessageOrder;
                    if ((sendOrder != null) && (sendOrder.Message == message))
                    {
                        this.PlayerOrderService.RemoveOrder(sendOrder);
                    }
                }
                if (CurrentRelationship.Outbox == message)
                {
                    CurrentRelationship.Outbox = null;
                }
                if (CurrentRelationship.ReplyOutbox == message)
                {
                    CurrentRelationship.ReplyOutbox = null;
                }
            }
        }

        private void EditMessageCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (!IsCurrentRelationshipNull && IsCurrentMessageOutgoing && !IsCurrentMessageDraft);
        }

        private void EditMessageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (!IsCurrentRelationshipNull && IsCurrentMessageOutgoing && !IsCurrentMessageDraft)
            {
                if (CurrentMessage == CurrentRelationship.Outbox)
                {
                    RetractMessage(CurrentMessage);
                    OutboxDraft = CurrentMessage;
                }
                else if (CurrentMessage == CurrentRelationship.ReplyOutbox)
                {
                    RetractMessage(CurrentMessage);
                    ReplyDraft = CurrentMessage;
                }
                UpdateMessage();
            }
        }

        private void OnRelationshipChanging(object sender, CurrentChangingEventArgs e)
        {
            if (e.IsCancelable && !DiscardDrafts())
            {
                e.Cancel = true;
            }
        }

        private void OnIncomingMessageChanged(object sender, EventArgs e)
        {
            CurrentMessage = CollectionViewSource.GetDefaultView(_messages).CurrentItem as DiplomaticMessage;
        }

        private void CounterProposalCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!IsCurrentMessageNull && !HasOutboxDraft)
            {
                if (IsCurrentMessageOutgoing)
                {
                    e.CanExecute = CurrentMessage.IsReply && !CurrentMessage.IsCounterProposal;
                }
                else
                {
                    e.CanExecute = CurrentMessage.IsProposal;
                }
            }
            else
            {
                e.CanExecute = false;
            }
        }

        private void CounterProposalCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if ((this.CurrentView == DiplomacyScreenViewType.Inbox)
                && (this.CurrentRelationship != null)
                && this.CurrentRelationship.HasIncomingMessage
                && (this.CurrentRelationship.Inbox.MessageType == DiplomaticMessageType.Proposal)
                && !this.CurrentRelationship.HasOutgoingMessage
                && !this.HasOutboxDraft)
            {
                this.OutboxDraft = DiplomaticMessage.CreateCounterProposal(
                    this.CurrentRelationship.Inbox.Proposal,
                    this.CurrentRelationship.Demeanor);
            }
        }

        private void RejectProposalCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!IsCurrentMessageNull && !HasOutboxDraft && CurrentMessage.IsProposal)
            {
                if (IsCurrentMessageOutgoing)
                {
                    e.CanExecute = (CurrentMessage.MessageType == DiplomaticMessageType.ProposalAcceptance);
                }
                else
                {
                    e.CanExecute = true;
                }
            }
            else
            {
                e.CanExecute = false;
            }
        }

        private void RejectProposalCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (!IsCurrentMessageNull && !HasOutboxDraft && CurrentMessage.IsProposal)
            {
                if (!IsCurrentMessageOutgoing
                    || (CurrentMessage.MessageType == DiplomaticMessageType.ProposalAcceptance)
                    || CurrentMessage.IsCounterProposal)
                {
                    DiplomaticMessage message = DiplomaticMessage.CreateProposalRejection(
                        CurrentMessage.Proposal,
                        this.CurrentRelationship.Demeanor);
                    SendMessage(message);
                    CurrentMessage = CurrentRelationship.ReplyOutbox;
                }
            }
        }

        private void TerminateAgreementCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            // TODO
        }

        private void AcceptProposalCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!IsCurrentMessageNull && !HasOutboxDraft && CurrentMessage.IsProposal)
            {
                if (IsCurrentMessageOutgoing)
                {
                    e.CanExecute = (CurrentMessage.MessageType == DiplomaticMessageType.ProposalRejection);
                }
                else
                {
                    e.CanExecute = true;
                }
            }
            else
            {
                e.CanExecute = false;
            }
        }

        private void AcceptProposalCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (!IsCurrentMessageNull && !HasOutboxDraft && CurrentMessage.IsProposal)
            {
                if (!IsCurrentMessageOutgoing
                    || (CurrentMessage.MessageType == DiplomaticMessageType.ProposalRejection)
                    || CurrentMessage.IsCounterProposal)
                {
                    DiplomaticMessage message = DiplomaticMessage.CreateProposalAcceptance(
                        CurrentMessage.Proposal,
                        this.CurrentRelationship.Demeanor);
                    SendMessage(message);
                    CurrentMessage = CurrentRelationship.ReplyOutbox;
                }
            }
        }

        private bool CanSendMessage
        {
            get
            {
                return ((this.CurrentRelationship != null)
                        && (this.CurrentView == DiplomacyScreenViewType.Outbox)
                        && !this.HasOutboxDraft);
            }
        }

        private bool CanIssueThreat
        {
            get
            {
                if (this.CurrentRelationship != null)
                {
                    return (this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.ThreatAggression)
                            || this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.ThreatAssault)
                            || this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.ThreatExpansion)
                            || this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.ThreatSabotage)
                            || this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.ThreatSpying));
                }
                return false;
            }
        }

        private void MakeProposalCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (CanSendMessage && this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.Proposal));
        }

        private void MakeProposalCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if ((this.CurrentRelationship != null) && (this.CurrentView == DiplomacyScreenViewType.Outbox) && !this.HasOutboxDraft)
            {
                this.OutboxDraft = DiplomaticMessage.CreateProposal(
                    this.PlayerCiv,
                    this.CurrentRelationship.OtherCiv,
                    this.CurrentRelationship.Demeanor);
            }
        }

        private void DeclareWarCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (CanSendMessage && this.CurrentRelationship.CanSendMessage(DiplomaticMessageType.WarDeclaration));
        }

        private void DeclareWarCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if ((this.CurrentRelationship != null) && (this.CurrentView == DiplomacyScreenViewType.Outbox) && !this.HasOutboxDraft)
            {
                this.OutboxDraft = new DiplomaticMessage(
                    this.PlayerCiv,
                    this.CurrentRelationship.OtherCiv,
                    DiplomaticMessageType.WarDeclaration,
                    this.CurrentRelationship.Demeanor);
            }
        }

        private void IssueThreatCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanIssueThreat;
        }

        private void IssueThreatCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (CanIssueThreat)
            {
                this.OutboxDraft = new DiplomaticMessage(
                    PlayerCiv,
                    this.CurrentRelationship.OtherCiv,
                    DiplomaticMessageType.ThreatExpansion);
            }
        }

        private void DenounceCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
        }

        private void DenounceCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
        }

        private void CommendCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
        }

        private void CommendCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
        }

        private void CancelMessageCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsCurrentMessageOutgoing;
        }

        private void CancelMessageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (IsCurrentMessageOutgoing)
            {
                if (IsCurrentMessageDraft)
                {
                    if (HasOutboxDraft && (OutboxDraft == CurrentMessage))
                    {
                        OutboxDraft = null;
                    }
                    else if (HasReplyDraft && (ReplyDraft == CurrentMessage))
                    {
                        ReplyDraft = null;
                    }
                }
                else if (!IsCurrentRelationshipNull)
                {
                    RetractMessage(CurrentMessage);
                    CurrentMessage = null;
                }
            }
        }

        private void SendMessageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (!IsCurrentRelationshipNull && IsCurrentMessageOutgoing && IsCurrentMessageDraft && CurrentMessage.IsValid)
            {
                if (CurrentMessage.IsReply)
                {
                    SendMessage(this.ReplyDraft);
                    ReplyDraft = null;
                    CurrentMessage = CurrentRelationship.ReplyOutbox;
                }
                else
                {
                    SendMessage(this.OutboxDraft);
                    OutboxDraft = null;
                    CurrentMessage = CurrentRelationship.Outbox;
                }
            }
        }

        private void SendMessageCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (!IsCurrentRelationshipNull && IsCurrentMessageOutgoing && IsCurrentMessageDraft && CurrentMessage.IsValid);
        }

        private void OnRelationshipChanged(object sender, EventArgs e)
        {
            this.CurrentRelationship = _relationshipsView.CurrentItem as Relationship;
        }

        private void Reset()
        {
            this.CurrentRelationship = null;
            UpdateRelationshipsList();
            UpdateMessagesList();
        }

        private void UpdateRelationshipsList()
        {
            Func<Relationship, bool> filter = o => o.IsContactMade;
            switch (CurrentView)
            {
                case DiplomacyScreenViewType.Agreements:
                    filter = o => (o.ActiveAgreements.Count > 0);
                    break;
                case DiplomacyScreenViewType.Overview:
                    filter = o => false;
                    break;
            }
            if (_relationshipsView != null)
            {
                _relationshipsView.CurrentChanged -= OnRelationshipChanged;
                _relationshipsView.CurrentChanging -= OnRelationshipChanging;
            }
            _relationshipsView = CollectionViewSource.GetDefaultView(
                GameContext.Current.Relationships.GetRelationshipsForCiv(PlayerCiv)
                    .AsParallel()
                    .Where(filter)
                    .OrderBy(o => o.OtherCiv.LongName)
                    .Select(o => o));
            _relationshipsView.CurrentChanged += OnRelationshipChanged;
            _relationshipsView.CurrentChanging += OnRelationshipChanging;
            if (_relationshipsView.IsCurrentBeforeFirst || _relationshipsView.IsCurrentAfterLast)
            {
                _relationshipsView.MoveCurrentToFirst();
            }
            this.DataContext = _relationshipsView;
        }

        private void SetMessageParameterCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ((e.Parameter is DiplomaticMessage) && (e.Parameter == OutboxDraft));
        }

        private void SetMessageParameterCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DiplomaticMessage message = e.Parameter as DiplomaticMessage;
            if (message != null)
            {
                // TODO: Get possible message parameters.
            }
        }

        protected void OnCurrentViewChanged()
        {
            UpdateRelationshipsList();

            switch (CurrentView)
            {
                case DiplomacyScreenViewType.Inbox:
                    UpdateMessagesList();
                    this.CurrentMessage = CollectionViewSource.GetDefaultView(_messages).CurrentItem as DiplomaticMessage;
                    break;
                case DiplomacyScreenViewType.Outbox:
                case DiplomacyScreenViewType.Agreements:
                    this.CurrentRelationship = _relationshipsView.CurrentItem as Relationship;
                    break;
            }

            if (CurrentView == DiplomacyScreenViewType.Inbox)
            {
                if (CollectionViewSource.GetDefaultView(_messages).IsCurrentBeforeFirst)
                    CollectionViewSource.GetDefaultView(_messages).MoveCurrentToFirst();
            }
            else
            {
                CollectionViewSource.GetDefaultView(_messages).MoveCurrentToPosition(-1);
                if (_relationshipsView.IsCurrentBeforeFirst || _relationshipsView.IsCurrentAfterLast)
                    _relationshipsView.MoveCurrentToFirst();
            }

            if (CurrentViewChanged != null)
                CurrentViewChanged(this, EventArgs.Empty);
        }

        private void UpdateMessagesList()
        {
            _messages.Clear();
            _messages.AddRange(from Relationship relationship in _relationshipsView
                               let messsage = relationship.Inbox ?? relationship.ReplyInbox
                               where messsage != null
                               orderby relationship.OtherCiv.LongName
                               select messsage);
        }

        private void UpdateMessage()
        {
            if (!IsCurrentMessageNull)
            {
                TextBlock messageText = CreateMessageText(this.CurrentMessage);
                _messageTextPresenter.Content = messageText;
                if (!this.CurrentMessage.IsGift && this.CurrentMessage.HasOffers)
                {
                    _proposalOffersLeadInPresenter.Content = new CivString(
                        this.CurrentMessage.Sender,
                        CivString.DiplomacyCategory,
                        "DIPLOMAT_PROPOSAL_OFFERS_LEAD_IN");
                }
                else
                {
                    _proposalOffersLeadInPresenter.Content = null;
                }
                if (!this.CurrentMessage.IsDemand && this.CurrentMessage.HasDemands)
                {
                    _proposalDemandsLeadInPresenter.Content = new CivString(
                        this.CurrentMessage.Sender,
                        CivString.DiplomacyCategory,
                        "DIPLOMAT_PROPOSAL_DEMANDS_LEAD_IN");
                }
                else
                {
                    _proposalDemandsLeadInPresenter.Content = null;
                }
            }

            _stipulations.Clear();
            _mutualStipulationList.Items.Clear();
            _offerStipulationList.Items.Clear();
            _demandStipulationList.Items.Clear();

            if (!IsCurrentMessageNull && CurrentMessage.IsProposal)
            {
                IList<Stipulation> offerStipulations = CurrentRelationship.GetAvailableStipulations(
                    CurrentMessage.Proposal.OfferStipulations,
                    CurrentMessage.Proposal.DemandStipulations,
                    false);
                IList<Stipulation> demandStipulations = CurrentRelationship.GetAvailableStipulations(
                    CurrentMessage.Proposal.OfferStipulations,
                    CurrentMessage.Proposal.DemandStipulations,
                    true);
                foreach (Stipulation demandStipulation in demandStipulations)
                {
                    StipulationListItem listItem = null;
                    if (demandStipulation.IsMutual)
                    {
                        if (!offerStipulations.Contains(demandStipulation))
                        {
                            listItem = new StipulationListItem(
                                StipulationListItem.MutualCategory,
                                (Stipulation)Activator.CreateInstance(demandStipulation.GetType()));
                        }
                    }
                    else
                    {
                        listItem = new StipulationListItem(
                            StipulationListItem.DemandsCategory,
                            (Stipulation)Activator.CreateInstance(demandStipulation.GetType()));
                    }
                    if (listItem != null)
                    {
                        _stipulations.Add(listItem);
                    }
                }
                foreach (Stipulation offerStipulation in offerStipulations)
                {
                    StipulationListItem listItem;
                    if (offerStipulation.IsMutual)
                    {
                        listItem = new StipulationListItem(
                            StipulationListItem.MutualCategory,
                            (Stipulation)Activator.CreateInstance(offerStipulation.GetType()));
                    }
                    else
                    {
                        listItem = new StipulationListItem(
                            StipulationListItem.OffersCategory,
                            (Stipulation)Activator.CreateInstance(offerStipulation.GetType()));
                    }
                    _stipulations.Add(listItem);
                }
                foreach (Stipulation offerStipulation in CurrentMessage.Proposal.OfferStipulations)
                {
                    if (offerStipulation.IsMutual)
                    {
                        _mutualStipulationList.Items.Add(CreateStipulationContainer(offerStipulation));
                    }
                    else
                    {
                        _offerStipulationList.Items.Add(CreateStipulationContainer(offerStipulation));
                    }
                }
                foreach (Stipulation demandStipulation in CurrentMessage.Proposal.DemandStipulations)
                {
                    if (demandStipulation.IsMutual)
                    {
                        _mutualStipulationList.Items.Add(CreateStipulationContainer(demandStipulation));
                    }
                    else
                    {
                        _demandStipulationList.Items.Add(CreateStipulationContainer(demandStipulation));
                    }
                }
            }
        }

        private static bool FilterIncoming(object obj)
        {
            Relationship relationship = obj as Relationship;
            return ((relationship != null)
                    && (relationship.HasIncomingMessage || relationship.HasIncomingReply));
        }

        private static bool FilterAgreements(object obj)
        {
            Relationship relationship = obj as Relationship;
            return ((relationship != null) && (relationship.ActiveAgreements.Count > 0));
        }

        private static bool FilterAllKnown(object obj)
        {
            Relationship relationship = obj as Relationship;
            return ((relationship != null) && relationship.IsContactMade);
        }

        private void SetCurrentViewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter is DiplomacyScreenViewType)
            {
                DiplomacyScreenViewType viewType = (DiplomacyScreenViewType)e.Parameter;
                if (!DiscardDrafts())
                {
                    return;
                }
                this.CurrentView = (DiplomacyScreenViewType)e.Parameter;
            }
        }

        private bool DiscardDrafts()
        {
            return DiscardDrafts(true);
        }

        private bool DiscardDrafts(bool askPlayer)
        {
            if (HasOutboxDraft || HasReplyDraft)
            {
                if (askPlayer)
                {
                    MessageDialogResult result = MessageDialog.Show(
                           "Unsent Message",
                           "The current message has not been sent.  Do you want to discard it?",
                           MessageDialogButtons.YesNo);
                    if (result == MessageDialogResult.No)
                    {
                        return false;
                    }
                    if ((this.HasOutboxDraft && (this.CurrentMessage == this.OutboxDraft))
                        || (this.HasReplyDraft && (this.CurrentMessage == this.ReplyDraft)))
                    {
                        this.CurrentMessage = null;
                    }
                }
                this.OutboxDraft = null;
                this.ReplyDraft = null;
            }
            return true;
        }

        private void ResetDiplomacyGraphCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            _civRelationshipGraph.CenterObject = _playerCivNode;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _civRelationshipGraph = GetTemplateChild("PART_CivRelationshipGraph") as NodeGraph;
            _messageTextPresenter = GetTemplateChild("PART_MessageTextPresenter") as ContentPresenter;
            _proposalOffersLeadInPresenter = GetTemplateChild("PART_ProposalOffersLeadInPresenter") as ContentPresenter;
            _proposalDemandsLeadInPresenter = GetTemplateChild("PART_ProposalDemandsLeadInPresenter") as ContentPresenter;
            _stipulationList = GetTemplateChild("PART_StipulationList") as ItemsControl;
            _mutualStipulationList = GetTemplateChild("PART_MutualStipulationList") as ItemsControl;
            _offerStipulationList = GetTemplateChild("PART_OfferStipulationList") as ItemsControl;
            _demandStipulationList = GetTemplateChild("PART_DemandStipulationList") as ItemsControl;
            _incomingMessageList = GetTemplateChild("PART_IncomingMessageList") as ItemsControl;
            _civilizationList = GetTemplateChild("PART_CivilizationList") as ItemsControl;
            
            _commDetailsBorder = GetTemplateChild("PART_CommDetailsBorder") as Decorator;

            if (_civRelationshipGraph != null)
            {
                _civRelationshipGraph.NodesBindingPath = "Children";
                _civRelationshipGraph.SetValue(ForegroundProperty, Brushes.White);
                _civRelationshipGraph.LinePen = new Pen(Brushes.White, 2.0);
                _civRelationshipGraph.PenSelector = new CivRelationshipPenSelector();
            }
            if (_stipulationList != null)
            {
                _stipulationList.ItemsSource = _stipulations;
            }
            if (_incomingMessageList != null)
            {
                _incomingMessageList.DataContext = _messages;
            }
        }

        public override void RefreshScreen()
        {
            base.RefreshScreen();
            BuildCivRelationshipGraph();
            UpdateRelationshipsList();
            UpdateMessagesList();
            UpdateMessage();
        }

        void CivRelationshipGraph_NodeClick(object sender, RoutedEventArgs e)
        {
            var buttonSource = e.OriginalSource as Button;
            if (buttonSource == null || !(buttonSource.Tag is CivRelationshipGraphNode))
                return;

            if (_civRelationshipGraph != null)
                _civRelationshipGraph.CenterObject = buttonSource.DataContext;
        }

        private void BuildCivRelationshipGraph()
        {
            int playerCivId = this.ClientContext.LocalPlayer.EmpireID;

            if (_civRelationshipGraph == null)
                return;

            var nodes = new List<CivRelationshipGraphNode>(GameContext.Current.Civilizations.Count);

            foreach (var civ in GameContext.Current.Civilizations)
            {
                var node = new CivRelationshipGraphNode(civ);
                nodes.Add(node);
                if (civ.CivID == playerCivId)
                    _playerCivNode = node;
            }

            for (var i = 0; i < nodes.Count; i++)
            {
                for (var j = i + 1; j < nodes.Count; j++)
                {
                    if (!DiplomacyHelper.IsContactMade(nodes[i].SourceCiv, nodes[j].SourceCiv) ||
                        !DiplomacyHelper.IsContactMade(this.ClientContext.LocalPlayer.Empire, nodes[i].SourceCiv) ||
                        !DiplomacyHelper.IsContactMade(this.ClientContext.LocalPlayer.Empire, nodes[j].SourceCiv))
                    {
                        continue;
                    }

                    if (!nodes[i].Children.Contains(nodes[j]))
                        nodes[i].Children.Add(nodes[j]);

                    if (!nodes[j].Children.Contains(nodes[i]))
                        nodes[j].Children.Add(nodes[i]);
                }
            }

            _civRelationshipGraph.CenterObject = nodes[this.ClientContext.LocalPlayerEmpire.Civilization.CivID];
        }

        public Relationship CurrentRelationship
        {
            get { return GetValue(CurrentRelationshipPropertyKey.DependencyProperty) as Relationship; }
            protected set { SetValue(CurrentRelationshipPropertyKey, value); }
        }

        public DiplomaticMessage CurrentMessage
        {
            get { return GetValue(CurrentMessagePropertyKey.DependencyProperty) as DiplomaticMessage; }
            protected set { SetValue(CurrentMessagePropertyKey, value); }
        }

        public bool IsCurrentMessageNull
        {
            get { return (bool)GetValue(IsCurrentMessageNullPropertyKey.DependencyProperty); }
            protected set { SetValue(IsCurrentMessageNullPropertyKey, value); }
        }

        public bool IsCurrentMessageIncoming
        {
            get { return (bool)GetValue(IsCurrentMessageIncomingPropertyKey.DependencyProperty); }
            protected set { SetValue(IsCurrentMessageIncomingPropertyKey, value); }
        }

        public bool IsCurrentMessageOutgoing
        {
            get { return (bool)GetValue(IsCurrentMessageOutgoingPropertyKey.DependencyProperty); }
            protected set { SetValue(IsCurrentMessageOutgoingPropertyKey, value); }
        }

        public bool IsCurrentMessageDraft
        {
            get { return (bool)GetValue(IsCurrentMessageDraftPropertyKey.DependencyProperty); }
            protected set { SetValue(IsCurrentMessageDraftPropertyKey, value); }
        }

        public bool IsCurrentRelationshipNull
        {
            get { return (bool)GetValue(IsCurrentRelationshipNullPropertyKey.DependencyProperty); }
            protected set { SetValue(IsCurrentRelationshipNullPropertyKey, value); }
        }

        private TextBlock CreateMessageText(DiplomaticMessage message)
        {
            if ((message != null) && ((message.Sender == PlayerCiv) || (message.Recipient == PlayerCiv)))
            {
                Regex regex = new Regex(@"(\$[_a-zA-Z][_a-zA-Z0-9]*)");
                string[] messageParts = regex.Split(message.MessageText.Value);
                TextBlock messageText = new TextBlock();
                messageText.TextWrapping = TextWrapping.Wrap;
                foreach (string messagePart in messageParts)
                {
                    Inline inline = null;
                    if (messagePart.StartsWith("$"))
                    {
                        if (messagePart.Equals("$MessageParameter", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = (message.Parameter != null)
                                ? new Run(message.Parameter.ToString())
                                : new Run("???");
                            if (message == OutboxDraft)
                            {
                                Hyperlink link = new Hyperlink(inline);
                                link.Command = SetMessageParameterCommand;
                                link.CommandParameter = message;
                                inline = link;
                            }
                        }
                        else if (messagePart.Equals("$Sender", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = new Run(message.Sender.LongName);
                        }
                        else if (messagePart.Equals("$Recipient", StringComparison.OrdinalIgnoreCase))
                        {
                            inline = new Run(message.Recipient.LongName);
                        }
                    }
                    else
                    {
                        inline = new Run(messagePart);
                    }
                    if (inline != null)
                    {
                        messageText.Inlines.Add(inline);
                    }
                }
                return messageText;
            }
            return null;
        }

        private void UpdateCurrentMessageStats()
        {
            IsCurrentMessageNull = (CurrentMessage == null);
            IsCurrentMessageIncoming = ((CurrentMessage != null) && (CurrentMessage.Recipient == PlayerCiv));
            IsCurrentMessageOutgoing = ((CurrentMessage != null) && (CurrentMessage.Sender == PlayerCiv));
            IsCurrentMessageDraft = ((CurrentMessage != null) && ((CurrentMessage == OutboxDraft) || (CurrentMessage == ReplyDraft)));
        }
    }

    public class CivRelationshipGraphNode
    {
        private string _toolTip;
        private readonly Civilization _sourceCiv;
        private readonly List<CivRelationshipGraphNode> _children;

        public Civilization SourceCiv
        {
            get { return _sourceCiv; }
        }

        public IList<CivRelationshipGraphNode> Children
        {
            get { return _children; }
        }

        public string ToolTip
        {
            get { return _toolTip; }
            set { _toolTip = value; }
        }

        public CivRelationshipGraphNode(Civilization sourceCiv)
        {
            if (sourceCiv == null)
                throw new ArgumentNullException("sourceCiv");
            _sourceCiv = sourceCiv;
            _children = new List<CivRelationshipGraphNode>();
        }
    }

    internal class CivRelationshipPenSelector : INodeGraphPenSelector
    {
        private const double PenThickness = 2.0;

        private static readonly Pen _fallbackPen;
        private static readonly Dictionary<DiplomacyStatus, Pen> _pens;

        static CivRelationshipPenSelector()
        {
            RelationshipStatusBrushConverter converter = new RelationshipStatusBrushConverter();
            _pens = new Dictionary<DiplomacyStatus, Pen>();
            _fallbackPen = new Pen(Brushes.Gainsboro, PenThickness);
            if (_fallbackPen.CanFreeze)
                _fallbackPen.Freeze();
            foreach (DiplomacyStatus status in EnumHelper.GetValues<DiplomacyStatus>())
            {
                Brush brush = converter.Convert(status, null, null, null) as Brush;
                if (brush == null)
                    continue;
                if (brush.CanFreeze)
                    brush.Freeze();
                _pens[status] = new Pen(brush, PenThickness);
                if (_pens[status].CanFreeze)
                    _pens[status].Freeze();
            }
        }

        #region INodeGraphPenSelector Members
        public Pen GetPen(object parentNode, object childNode)
        {
            CivRelationshipGraphNode node1 = parentNode as CivRelationshipGraphNode;
            CivRelationshipGraphNode node2 = childNode as CivRelationshipGraphNode;
            if ((node1 != null) && (node2 != null))
            {
                Pen pen;
                DiplomacyStatus diplomacyStatus = GameContext.Current.DiplomacyStatus[node1.SourceCiv, node2.SourceCiv];
                if (_pens.TryGetValue(diplomacyStatus, out pen))
                    return pen;
            }
            return _fallbackPen;
        }
        #endregion
    }

    internal class StipulationListItem
    {
        public const string OffersCategory = "Offers";
        public const string DemandsCategory = "Demands";
        public const string MutualCategory = "Treaties";

        private readonly string _category;
        private readonly Stipulation _value;

        public StipulationListItem(string category, Stipulation value)
        {
            this._category = category;
            this._value = value;
        }

        public string Category
        {
            get { return this._category; }
        }

        public Stipulation Value
        {
            get { return this._value; }
        }
    }

    [ValueConversion(typeof(Stipulation), typeof(string))]
    public class AgreementStipulationDescriptionConverter : IValueConverter
    {
        private readonly IRegionManager _regionManager;
        private readonly IClientContext _clientContext;

        public AgreementStipulationDescriptionConverter()
        {
            _regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
            _clientContext = ServiceLocator.Current.GetInstance<IClientContext>();
        }

        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (!_clientContext.IsGameInPlay)
                return null;

            var isRecipient = false;
            var stipulation = (Stipulation)value;

            Agreement agreement = null;
            
            if (_regionManager.Regions.ContainsRegionWithName(ClientRegions.GameScreens))
            {
                var diplomacyScreen = _regionManager.Regions[ClientRegions.GameScreens].GetView(StandardGameScreens.DiplomacyScreen) as DiplomacyScreen;
                if (diplomacyScreen != null)
                {
                    agreement = diplomacyScreen.CurrentRelationship.ActiveAgreements.FirstOrDefault(
                        o => o.Proposal.AllStipulations.Contains(stipulation));
                    isRecipient = (agreement.Recipient.CivID == _clientContext.LocalPlayerEmpire.CivilizationID);
                }
            }

            if (stipulation != null)
            {
                var regex = new Regex(@"(\$[_a-zA-Z][_a-zA-Z0-9)]*)|(\$\([^\)]*\))");
                var civForTextLookup = _clientContext.LocalPlayerEmpire.Civilization;
                
                if ((agreement != null) && isRecipient)
                    civForTextLookup = agreement.Sender;

                var description = new CivString(civForTextLookup, CivString.DiplomacyCategory, stipulation.LongDescription.Key);
                var stipulationParts = regex.Split(description.Value);
                var stipulationText = new StringBuilder();

                foreach (var stipulationPart in stipulationParts)
                {
                    if (stipulationPart.StartsWith("$(") && stipulationPart.EndsWith(")"))
                    {
                        stipulationText.Append(stipulationPart.Substring(2, stipulationPart.Length - 3));
                    }
                    else if (stipulationPart.StartsWith("$"))
                    {
                        if (stipulationPart.Equals("$Parameter", StringComparison.OrdinalIgnoreCase))
                        {
                            if (stipulation.HasParameter && stipulation.IsParameterValid)
                            {
                                if (stipulation.Parameter is Civilization)
                                {
                                    stipulationText.Append(((Civilization)stipulation.Parameter).LongName);
                                }
                                else if (stipulation.Parameter is UniverseObject)
                                {
                                    stipulationText.Append(((UniverseObject)stipulation.Parameter).Name);
                                }
                                else
                                {
                                    stipulationText.Append(stipulation.Parameter.ToString());
                                }
                            }
                            else
                            {
                                stipulationText.Append("???");
                            }
                        }
                        else if (stipulationPart.Equals("$Sender", StringComparison.OrdinalIgnoreCase))
                        {
                            stipulationText.Append((agreement != null) ? agreement.Sender.LongName : "???");
                        }
                        else if (stipulationPart.Equals("$Recipient", StringComparison.OrdinalIgnoreCase))
                        {
                            stipulationText.Append((agreement != null) ? agreement.Recipient.LongName : "???");
                        }
                        else if (stipulationPart.Equals("$Offerer", StringComparison.OrdinalIgnoreCase))
                        {
                            Civilization offerer = null;
                            if (agreement != null)
                            {
                                if (stipulation is MembershipStipulation)
                                {
                                    offerer = agreement.Sender.IsEmpire
                                                  ? agreement.Sender
                                                  : agreement.Recipient;
                                }
                                else
                                {
                                    offerer = agreement.Proposal.DemandStipulations.Contains(stipulation)
                                                  ? agreement.Recipient
                                                  : agreement.Sender;
                                }
                            }
                            stipulationText.Append((offerer != null) ? offerer.LongName : "???");
                        }
                        else if (stipulationPart.Equals("$Receiver", StringComparison.OrdinalIgnoreCase))
                        {
                            if (agreement != null)
                            {
                                var receiver = agreement.Proposal.DemandStipulations.Contains(stipulation)
                                                   ? agreement.Sender
                                                   : agreement.Recipient;
                                stipulationText.Append(receiver.LongName);
                            }
                            else
                            {
                                stipulationText.Append("???");
                            }
                        }
                        else if (stipulationPart.Equals("$Duration", StringComparison.OrdinalIgnoreCase))
                        {
                            stipulationText.Append(String.Format(stipulation.GetDurationString(), stipulation.Duration));
                        }
                    }
                    else
                    {
                        stipulationText.Append(stipulationPart);
                    }
                }
                return stipulationText.ToString();
            }
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
        #endregion
    }
}
